﻿

using System.Data.OleDb;
using System.Data;
using System.IO;
using System;
using System.Collections.Generic;


namespace DXL_Library
{

    /// <summary>
    /// Carga archivos de XL en memoria en un objeto unico de tipo DXL_SpreadSheet
    /// </summary>
    public class DXL_Library
    {
        public static int ReadLineStreamReader(StreamReader sr, int actual, out string linea, out string msg)
        {
            int numLinea;

            numLinea = actual;
            linea = null;
            msg = string.Empty;

            if (sr == null)
            {
                msg = "El StreamReader no puede ser nulo";
                throw new ArgumentException(msg);
            }

            try
            {
                linea = sr.ReadLine();

                if (linea != null)
                {
                    linea = linea.Trim();
                    numLinea++;
                }
            }
            catch (Exception ex)
            {
                numLinea = -999;
                msg = string.Format("Error tecnico {0}", ex.Message);
            }
            return numLinea;

        }
        /// <summary>""
        /// Determina si el texto de una colmuna es "FXX" donde XX puede ser "1"
        /// </summary>
        /// <param name="valor"></param>
        /// <returns></returns>
        public static bool NombreColumnaVacia(string valor)
        {
            bool resp;

            resp = false;

            if (valor != null)
            {
                if ((valor.Length <= 3) && (valor.StartsWith("F")))
                {
                    int x;
                    if (int.TryParse(valor.Substring(1), out x))
                    {
                        resp = true;
                    }

                }

            }
            return resp;
        }
       
        /// <summary>
        /// Esta funcion Crea una hoja de calculo en memoria con dos posibles funcionalidades: 
        ///     - Para interpreta la especificacion del archivo de XL a cargar y luego cargarlo con LOAD
        ///     - Crear una hoja de calculo en memoria para llenarla con datos y mostrarla con MostrarDatos.aspx.cs
        /// </summary>
        /// <param name="archEspecificacion">la ruta al archivo de especificacion</param>
        /// <returns>true si la especificacion cumple la sintaxis</returns>
        public static DXL_SpreadSheet CrearHojaCalculo(DXL_LibraryParameters p)
        {
            string archEspecificacion;
            StreamReader sr;
            string linea;
            DXL_SpreadSheet hc;
            DXL_Sheet h;
            DXL_Sheet actual;
            DXL_Grid g;
            string msg;
            string seccion;
            int numLinea;

            if (p == null)
            {
                throw new ArgumentException("el parametro DXL_LibraryParameters no puede ser nulo");
            }
            
            archEspecificacion = p.pathToSpecsFile;
            seccion = null;
            sr = null;
            hc = null;

            numLinea = 0;
            linea = null;

            try
            {


                hc = new DXL_SpreadSheet();
                hc.parametros = p;
                hc.AgregarLogCarga(String.Format("Iniciando proceso de carga de la especificacion:{0}\n", archEspecificacion));

                sr = new StreamReader(archEspecificacion);
                hc.Hojas = new List<DXL_Sheet>();

                actual = new DXL_Sheet();

                do
                {
                    numLinea = ReadLineStreamReader(sr, numLinea, out linea, out msg);
                    if (linea != null)
                    {
                        linea = linea.Trim();
                        if (linea.Length == 0)
                        {
                            //IGNORAR LAS LINEAS EN BLANCO !!
                        }
                        //es una SECCION al estilo archivo .INI
                        else if (linea.StartsWith("["))
                        {
                            seccion = linea;
                        }
                        //es un comentario
                        else if (linea.StartsWith("/*"))
                        {
                            do
                            {
                                numLinea = ReadLineStreamReader(sr, numLinea, out linea, out msg);
                            } while ((linea != null) && (!linea.EndsWith("*/")));
                        }
                        else
                        {

                            if (seccion.ToUpper() == "[HOJAS]")
                            {
                                #region cargarHOJAS
                                //ya se leyo el NOMBRE

                                h = new DXL_Sheet();
                                h.Nombre = linea;

                                //determinar si es REQUERIDA
                                numLinea = ReadLineStreamReader(sr, numLinea, out linea, out msg);
                                if (linea != null)
                                {
                                    h.EsRequerida = (linea.ToUpper() == "SI");

                                    //leer el ID_HOJA
                                    numLinea = ReadLineStreamReader(sr, numLinea, out linea, out msg);

                                    if (linea != null)
                                    {
                                        h.IdHoja = int.Parse(linea);

                                        hc.Hojas.Add(h);
                                    }
                                    else
                                    {
                                        throw new Exception("Debe especificar el ID_HOJA de la hoja de calculo");
                                    }
                                    //leer el ";"
                                    numLinea = ReadLineStreamReader(sr, numLinea, out linea, out msg);
                                }
                                else
                                {
                                    throw new Exception("Debe especificar si es REQUERIDA la hoja de calculo");
                                }
                                #endregion
                            }
                            else if (seccion.ToUpper() == "[GRILLAS]")
                            {
                                #region cargarGRILLAS

                                //ya se leyo ID_HOJA

                                int pos;

                                //se busca la HOJA actual dentro las especificadas

                                actual.IdHoja = int.Parse(linea);

                                pos = hc.Hojas.IndexOf(actual);

                                if (pos >= 0)
                                {
                                    h = hc.Hojas[pos];

                                    if (h.Grillas == null)
                                    {
                                        h.Grillas = new List<DXL_Grid>();
                                    }
                                    g = new DXL_Grid();

                                    //leer el RANGO DE TITULOS
                                    numLinea = ReadLineStreamReader(sr, numLinea, out linea, out msg);

                                    if (linea != null)
                                    {
                                        if (Util.ComienzaPorComillas(linea))
                                        {
                                            g.TitulosColumnas = Util.RenglonEnListaStrings(linea);
                                            g.PrimerRenglonConTitulos = false;
                                        }
                                        else
                                        {
                                            g.TitulosRango = linea;
                                            g.PrimerRenglonConTitulos = true;
                                        }

                                        //leer el nombre de la grilla
                                        numLinea = ReadLineStreamReader(sr, numLinea, out linea, out msg);


                                        if (linea != null)
                                        {
                                            g.NombreFijo = Util.ComienzaPorComillas(linea);

                                            if (g.NombreFijo)
                                            {
                                                g.Nombre = Util.RemoverComillas(linea);
                                            }
                                            else
                                            {
                                                g.Nombre = linea;
                                            }

                                            //leer el RANGO tipo excel donde estan los datos de esta grilla o pueden venir datos FIJOS como por ejemplo la primera columna 
                                            numLinea = ReadLineStreamReader(sr, numLinea, out linea, out msg);

                                            if (linea != null)
                                            {
                                                if (Util.ComienzaPorComillas(linea))
                                                {
                                                    g.ValoresPrimerColumna = Util.RenglonEnListaStrings(linea);
                                                }
                                                else
                                                {
                                                    g.Rango = linea;
                                                }
                                                h.Grillas.Add(g);
                                            }
                                            else
                                            {
                                                throw new Exception(string.Format("Debe especificar el RANGO dentro de la hoja de calculo {0}", h.Nombre));
                                            }
                                            //NUEVO leer el renglon con tipo de datos
                                            numLinea = ReadLineStreamReader(sr, numLinea, out linea, out msg);
                                            string auxTipos;

                                            auxTipos = linea;
                                            if (linea != null)
                                            {
                                                g.TiposColumnas = Util.RenglonEnListaStrings(linea);
                                            }

                                            //NUEVO leer el renglon con formato de datos
                                            numLinea = ReadLineStreamReader(sr, numLinea, out linea, out msg);
                                            string auxFormatos;

                                            auxFormatos = linea;
                                            if (linea != null)
                                            {
                                                g.FormatosColumna = Util.RenglonEnListaStrings(linea);
                                            }

                                            if((g.TiposColumnas != null) && (g.FormatosColumna != null))
                                            {
                                                int numColTipos ;
                                                int numColFormato ;

                                                numColTipos = g.TiposColumnas.Count ;
                                                numColFormato = g.FormatosColumna.Count ;

                                                if (numColTipos != numColFormato)
                                                {
                                                    string aux ;

                                                    aux = string.Format("El numero de tipos de Columnas {0} no coincide con el numero de Formatos de Columna {1}\n---------------------\n{2}\n\n--------------------- VERSUS ---------------------\n\n{3}\n", numColTipos, numColFormato, auxTipos, auxFormatos);
                                                    throw new Exception(aux) ; 
                                                }
                                            }

                                            //leer el ";"
                                            numLinea = ReadLineStreamReader(sr, numLinea, out linea, out msg);
                                        }
                                        else
                                        {
                                            throw new Exception(string.Format("Debe especificar el NOMBRE del RANGO/GRILLA dentro de la hoja de calculo {0}", h.Nombre));
                                        }
                                    }
                                    else
                                    {
                                        throw new Exception(string.Format("Debe especificar el rango de los TITULOS a usar en la grilla o \"----\"en caso de que ya sean parte del rango de datos", h.Nombre));
                                    }
                                }
                                else
                                {
                                    throw new Exception(string.Format("El ID_HOJA especificado {0} no coincide con la seccion [HOJAS]", actual.IdHoja));
                                }
                                #endregion
                            }
                            else
                            {
                                throw new Exception(string.Format("la SECCION {0} no es soportada/valida", linea));
                            }

                        }
                    }
                } while (linea != null);


            }
            catch (FileNotFoundException fnfe)
            {
                hc.AgregarLogErrores(string.Format("El archivo de especificaciones '{0}' no fue encontrado:", fnfe.Message));
            }
            catch (FormatException fe)
            {
                hc.AgregarLogErrores(string.Format("Error en archivo de especificacioines: Error de formato : {0} -- Linea {1}\n{2}\n", fe.Message, numLinea, linea));
            }
            catch (Exception ex)
            {
                hc.AgregarLogErrores(string.Format("Error en archivo de especificacioines: {0} -- Linea {1}\n", ex.Message, numLinea));
            }
            finally
            {
                if (sr != null)
                {
                    sr.Close();
                }
            }

            //Verificar que la especificacion de la DXL_SpreadSheet tengo al menos un HOJA(Sheet) y una GRILLA
            if (hc != null)
            {
                //Si no tiene hojas
                if ((hc.Hojas == null) || (hc.Hojas.Count == 0))
                {
                    hc.AgregarLogErrores(string.Format("Debe especificar al menos un HOJA en la seccion [HOJAS]: {0}", archEspecificacion));
                }
                else
                {
                    //Tiene al menos una hoja...

                    //verificar que tenga al menos una grilla
                    if ((hc.Hojas[0].Grillas == null) || (hc.Hojas[0].Grillas.Count == 0))
                    {
                        hc.AgregarLogErrores(string.Format("Debe especificar al menos un GRILLA en la seccion [GRILLAS]: {0}", archEspecificacion));
                    }
                }
            }
            return hc;
        }
        public static void PartirRango(string unRangoExcel, out string celdaUno, out string celdaDos)
        {
            string[] strRango;

            strRango = unRangoExcel.Split(':');
            celdaUno = strRango[0];
            celdaDos = strRango[1];

        }
        public static void PartirRango(string unRangoExcel, out string columnaIni, out int filaIni, out string columnaFin, out int filaFin)
        {
            string txtIni;
            string txtFin;

            DXL_Library.PartirRango(unRangoExcel, out txtIni, out txtFin);

            int i;

            string aux;

            aux = txtIni;

            for (i = 0; Char.IsLetter(aux[i]); i++)
            {
                ;
            }
            columnaIni = aux.Substring(0, i);
            filaIni = Int16.Parse(aux.Substring(i));

            aux = txtFin;

            for (i = 0; Char.IsLetter(aux[i]); i++)
            {
                ;
            }
            columnaFin = aux.Substring(0, i);
            filaFin = Int16.Parse(aux.Substring(i));

        }
        public static int NumFilasRango(string rango)
        {
            int resp;
            int filA;
            int filB;
            string aux;

            PartirRango(rango, out aux, out filA, out aux, out filB);

            resp = filB - filA + 1;

            return resp;
        }
        public static int NumColumnasRango(string rango)
        {
            int resp;
            int fil;
            string auxA;
            string auxB;

            PartirRango(rango, out auxA, out fil, out auxB, out fil);

            resp = NumColumna(auxB) - NumColumna(auxA) + 1;

            return resp;
        }
        public static int NumColumna(string nomColumnaExcel)
        {
            int resp;
            int baseX;
            int potencia;
            int fact;

            baseX = ('Z' - 'A' + 1);

            //A,B,C....Z = 27 Y luego viene la coumna AA = 28, AB = 29..etc

            resp = 0;
            for (int i = 0; i < nomColumnaExcel.Length; i++)
            {
                potencia = nomColumnaExcel.Length - i - 1;
                fact = (int)Math.Pow(baseX, potencia);
                resp = resp + (nomColumnaExcel[i] - 'A' + 1) * fact;
            }
            resp--;


            return resp;
        }
       // public static bool DEBUG;


        public static DataTable CargarNombre(DXL_Grid g, OleDbConnection conexionExcel, DXL_Sheet h, int contHoja, int contGrilla, out string msg)
        {
            bool usarQueryNombre;

            usarQueryNombre = true;

            DXL_LibraryParameters p;

            p = new DXL_LibraryParameters(false, false);

            return CargarRango(g, usarQueryNombre, conexionExcel, h, p, contHoja, contGrilla, out msg);
        }
        public static DataTable CargarDatos(DXL_Grid g, OleDbConnection conexionExcel, DXL_Sheet h, DXL_LibraryParameters p, int contHoja, int contGrilla, out string msg)
        {
            bool usarQueryNombre;

            usarQueryNombre = false;
            return CargarRango(g, usarQueryNombre, conexionExcel, h, p, contHoja, contGrilla, out msg);
        }

        /// <summary>
        /// Grilla es diferente de null
        /// </summary>
        /// <param name="g"></param>
        /// <param name="usarQueryNombre"></param>
        /// <param name="conexionExcel"></param>
        /// <param name="h"></param>
        /// <param name="permiteCeldasVacias"></param>
        /// <param name="renglonPorRenglon"></param>
        /// <param name="contHoja"></param>
        /// <param name="contGrilla"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        public static DataTable CargarRango(DXL_Grid g, bool usarQueryNombre, OleDbConnection conexionExcel, DXL_Sheet h,DXL_LibraryParameters p, int contHoja, int contGrilla, out string msg)
        {
            OleDbDataAdapter commandExcel;
            DataSet dsExcel;
            DataTable resp;
            DataRow dr;

            int filaIni;
            int numFilas;
            string query;
            int contFil;
            int numCol;
            msg = string.Empty;

            resp = null;

            if (p.DONT_USE_BUGGY_LoadRowByRow)
            {

                numCol = g.NumColumnasRango();
                numFilas = g.NumFilasRango();

                resp = CrearColumnasDataTable(g, h, conexionExcel);

                filaIni = (g.ConTitulos()) ? 0 : 1;
                //cargar los datos al DataSet contenido en la Grilla RENGLON POR RENGLON y validando tipos
                for (contFil = filaIni; contFil < numFilas; contFil++)
                {
                    query = g.QueryPorRenglon(contFil, h);

                    commandExcel = new OleDbDataAdapter(query, conexionExcel);

                    dsExcel = new DataSet();


                    commandExcel.Fill(dsExcel);

                    if ((dsExcel.Tables != null) && (dsExcel.Tables[0].Rows != null))
                    {
                        dr = dsExcel.Tables[0].Rows[0];


                        if (RenglonTiposCorrectos(g, dr, numCol, contFil,p.allowEmptyCells, p.allowDataTypeEquivalence, contHoja, contGrilla, out msg))
                        {
                            //este metodo no retorno si tuvo exito o no ...tampoco parece reportar un throw de excepciones y ME FUNCIONOI !! 
                            //resp.ImportRow(dr);
                            AgregarFila(resp, dr);
                        }
                        else
                        {
                            resp = null;
                            break;
                            //msg
                        }
                    }
                    else
                    {
                        resp = null;
                        break;
                    }
                }
                /*no es necesario
                if (resp != null)
                {
                    resp.AcceptChanges();
                }
                 */
            }
            else
            {
                //Obtiene el SQL Query de TODO el rango a partir de la especificacion de la grilla
                query = (usarQueryNombre) ? g.QueryNombre(h) : g.Query(h);

                commandExcel = new OleDbDataAdapter(query, conexionExcel);

                dsExcel = new DataSet();

                try
                {
                    commandExcel.Fill(dsExcel);

                    if (dsExcel.Tables != null)
                    {
                        resp = dsExcel.Tables[0];
                    }
                }
                catch (Exception)
                {
                    resp = null;
                }
            }

            return resp;
        }
        public static DataTable CrearColumnasDataTable(DXL_Grid g, DXL_Sheet h, OleDbConnection conexionExcel)
        {
            DataTable resp;
            string query;
            OleDbDataAdapter commandExcel;
            DataSet dsExcel;

            resp = null;

            //Titulos fijos en la espec
            if (g.TitulosColumnas != null)
            {
                foreach (string tit in g.TitulosColumnas)
                {
                    resp = new DataTable();

                    resp.Columns.Add(tit);
                }
            }
            else
            {
                //Crear las COLUMNAS segun los titulos
                query = g.QueryTitulos(h);

                if (query != null)
                {

                    commandExcel = new OleDbDataAdapter(query, conexionExcel);

                    dsExcel = new DataSet();

                    commandExcel.Fill(dsExcel);

                    if (dsExcel.Tables != null)
                    {
                        resp = dsExcel.Tables[0].Clone();

                    }
                    else
                    {

                    }

                }
            }

            return resp;
        }
        public static bool AgregarFila(DataTable dt, DataRow dr)
        {
            bool exito;
            DataRow nueva;

            exito = false;

            if (dt == null)
            {
                throw new ArgumentException("DataTable no puede ser nulo");
            }
            if (dr == null)
            {
                throw new ArgumentException("DataRow no puede ser nulo");
            }
            try
            {


                if (dr.ItemArray != null)
                {
                    nueva = dt.NewRow();

                    for (int i = 0; i < dr.ItemArray.Length; i++)
                    {
                        nueva[i] = dr[i];
                    }

                    dt.Rows.Add(nueva);

                    exito = true;
                }


            }
            catch
            {


            }
            return exito;
        }
        public static string CrearRango(string iniCol, int iniFil, string finCol, int finFil)
        {
            string resp;

            resp = string.Format("{0}{1}:{2}{3}", iniCol, iniFil, finCol, finFil);

            return resp;
        }
        private static OleDbConnection AbrirExcel(string pathArchivo)
        {
            OleDbConnection conexionExcel;
            string cadena;

            cadena = string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source='{0}';Extended Properties=Excel 12.0;", pathArchivo);

            conexionExcel = null;
            try
            {
                conexionExcel = new OleDbConnection(cadena);
            }
            catch
            {

            }
            return conexionExcel;
        }
        public static DXL_SpreadSheet Load(DXL_LibraryParameters p)
            //string pathArchivo, string archEspecificacion, bool PermiteCeldasVacias, bool buscarEquivalenciasTipos, bool renglonPorRenglon)
        {
            bool error;
            DXL_SpreadSheet hc;

            hc = CrearHojaCalculo(p);
            hc.AgregarLogCarga(String.Format("Archivo de especificacion {0} cargado exitosamente\n", p.pathToSpecsFile));
            hc.RutaArch = p.pathToXLfile;

            error = Load(hc, p);

            return hc;
        }
        /// <summary>
        /// Carga un archivo de EXCEL y sus pestañas en una unica estructura DXL_SpreadSheet en memoria
        /// </summary>
        /// <param name="path"></param>
        /// <param name="archEspecificacion"></param>
        /// <param name="renglonPorRenglon"></param>
        /// <returns></returns>
        public static bool Load(DXL_SpreadSheet hc,DXL_LibraryParameters p)
        {
            //string pathArchivo, bool PermiteCeldasVacias, bool buscarEquivalenciasTipos, bool renglonPorRenglon
            DXL_Sheet hojaActual;
            int numGrilla;
            int numHoja;
            bool error;

            /*
            hc = Create(archEspecificacion);
            hc.RutaArch = pathArchivo;
            */
            error = false;
            string msg;
            string soloNomArch;

            soloNomArch = Util.SinRuta(p.pathToXLfile);

            msg = string.Empty;

            if (hc != null)
            {


                //recorrer la especificacion cargada e ir leyendo los datos en memoria
                OleDbConnection conexionExcel;

                conexionExcel = null;
                hojaActual = null;
                try
                {
                    conexionExcel = AbrirExcel(p.pathToXLfile);
                    numHoja = 1;

                    foreach (DXL_Sheet h in hc.Hojas)
                    {
                        hojaActual = h;

                        h.NumHoja = numHoja;

                        hc.AgregarLogCarga(String.Format("Cargando datos para la HOJA: {0}\n", h.Nombre));

                        //se garantizo que la primera hoja tiene al menos un GRILLA pero puede haber otras HOJAS sin grillas especificadas
                        if (h.Grillas != null)
                        {
                            numGrilla = 1;

                            foreach (DXL_Grid g in h.Grillas)
                            {
                                //Si el nombre de la grilla se toma a partir del EXCEL
                                if (!g.NombreFijo)
                                {
                                    DataTable dt;

                                    dt = CargarNombre(g, conexionExcel, h, numHoja, numGrilla, out msg);

                                    //El nombre de la COLUMNA del dataset es el CONCEPTO
                                    g.Nombre = dt.Columns[0].ToString();

                                }
                                else
                                {
                                    //quitar las comillas iniciales y finales (si la final no esta presente pues no la quita ;-)
                                    g.Nombre = Util.RemoverComillas(g.Nombre);
                                }
                                g.NumGrilla = numGrilla;

                                hc.AgregarLogCarga(String.Format("\tCargando datos para la GRILLA {0} \n", g.Nombre));

                                g.Datos = CargarDatos(g, conexionExcel, h, p, numHoja, numGrilla, out msg);

                                if (g.Datos == null)
                                {
                                    //error de validacion de tipos o similar
                                    hc.AgregarLogErrores(string.Format("Error en '{0}': {1}", soloNomArch, msg));
                                    error = true;
                                    break;
                                }
                                else
                                {
                                    hc.AgregarLogCarga(String.Format("\t{0} filas cargadas\n", g.Datos.Rows.Count));
                                }

                                numGrilla++;
                            }
                            if (error)
                            {
                                break;
                            }
                        }
                        numHoja++;
                    }
                }
                catch (Exception ex)
                {
                    //Aca es posible que se presenten expeciones cuando por ejemplo la HOJA solicitada no esta en el EXCEL
                    if (hojaActual != null)
                    {
                        if (hojaActual.EsRequerida)
                        {
                            if (ex is OleDbException)
                            {
                                // mostrar un error amigable extrayendolo de ex.Message el nombre de la hoja/pestaña no encontrada
                                hc.AgregarLogErrores(String.Format("La hoja \"{0}\" es REQUERIDA pero no se encontró en el archivo {1} ", NombrePestañaRango(ex.Message), soloNomArch));
                            }
                            else
                            {
                                hc.AgregarLogErrores(String.Format("Error: {0}\n", ex.Message));
                            }
                        }
                        else
                        {
                            hc.AgregarLogCarga(String.Format("La hoja \"{0}\" no se encontro en el archivo pero es OPCIONAL", hojaActual.Nombre));
                            //si no hay pestaña ..limpiar la especificacion de memoria
                            //hojaActual.Grillas.Clear();

                            //si la hoja es opcional y no viene en el arhivo ..quitarla de memoria
                            hc.Hojas.Remove(hojaActual);
                        }
                    }
                    else
                    {
                        hc.AgregarLogErrores(String.Format("Error: No se pudo recorrer las hojas/pestañas del archivo XL{0}. Revise si el archivo esta en uso por otro programa\n", soloNomArch));
                    }
                }
                finally
                {
                    if (conexionExcel != null)
                    {
                        conexionExcel.Close();
                    }
                }
            }
            //return hc;

            return error;
        }
        /// <summary>
        /// Extra el NOMBRE DE PESTAÑA dado un OleDbException.message
        /// </summary>
        /// <param name="exMensaje"></param>
        /// <returns></returns>
        public static string NombrePestañaRango(string exMensaje)
        {
            string resp;
            int posIni;
            int cuantos;

            /*
            "The Microsoft Access database engine could not find the object 'LIQUIDACION S-2 IMPORT 230$B4:F5'.
            Make sure the object exists and that you spell its name and the path name correctly. 
             * 
            If 'LIQUIDACION S-2 IMPORT 230$B4:F5' is not a local object, check your network connection or contact the server administrator."
             */
            resp = exMensaje;

            if (resp.Contains("\'"))
            {
                posIni = resp.IndexOf('\'');
                //cuantos = exMensaje.Substring(posIni+1).IndexOf('\'');
                cuantos = exMensaje.Substring(posIni + 1).IndexOf('$');

                resp = resp.Substring(posIni, cuantos + 1);
            }
            return resp;
        }
        public static bool DETECTAR_EXCEL_INSTALADO = true;


        public static bool RenglonTiposCorrectos(DXL_Grid g, DataRow dr, int numCol, int cualFil, bool permiteCeldasVacias, bool buscarEquivalenciasTipos, int contHoja, int contGrilla, out string msg)
        {
            int i;
            bool cumple;
            string tipoEsperado;
            object aux;

            cumple = true;
            msg = string.Empty;

            for (i = 0; (cumple && (i < numCol)); i++)
            {
                aux = dr[i];
                tipoEsperado = g.TiposColumnas[i];

                cumple = CoincidenTipos(aux, tipoEsperado, permiteCeldasVacias, buscarEquivalenciasTipos);

                if (!cumple)
                {
                    msg = string.Format("La celda con valor \'{0}\' es de tipo {1} pero debe ser {2} -- Hoja #{3}, Grilla #{4}, Rango {5}, Fila={6}, Columna={7}",
                        aux,
                        g.TipoNombreAmigable(aux.GetType().ToString()),
                        g.TipoNombreAmigable(tipoEsperado),
                        contHoja,
                        contGrilla,
                        g.Rango,
                        cualFil + 1,
                        i + 1);
                }
            }

            return cumple;
        }
        /*DEPENDENCIA DE EXCEL INSTALADO
        public static bool Validate(string archExcelconRuta, string arcEsp, bool permiteCeldasVacias, bool buscarEquivalenciasTipos, out string msg)
        {
            bool excelInstalado;
            bool valido;
            DXL_SpreadSheet specHC;
            Microsoft.Office.Interop.Excel.Application ObjExcel;
            Microsoft.Office.Interop.Excel.Workbook ObjWorkBook;

            ObjExcel = null;
            ObjWorkBook = null;

            msg = string.Empty;
            valido = true;

            excelInstalado = false;

            try
            {
                ObjExcel = new Microsoft.Office.Interop.Excel.Application();

                excelInstalado = true;

                ObjWorkBook = ObjExcel.Workbooks.Open(archExcelconRuta, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);

                specHC = CrearHojaCalculo(arcEsp);

                if ((ObjWorkBook != null) && (specHC != null))
                {
                    valido = CumpleSpec(ObjWorkBook, specHC, permiteCeldasVacias, buscarEquivalenciasTipos, out msg);
                }
            }

            catch (Exception ex)
            {
                if (excelInstalado)
                {
                    msg = string.Format("Error interno de validacion {0}\n{1}", ex.Message, ex.StackTrace);
                    valido = false;
                }
                else
                {
                    msg = "Excel debe estar instaldo para usar esta funcionalidad";
                    valido = true;
                }

            }
            finally
            {
                if (ObjWorkBook != null)
                {
                    ObjWorkBook.Close(Type.Missing, Type.Missing, Type.Missing);
                }
                if (ObjExcel != null)
                {
                    ObjExcel.Quit();
                }
            }

            return valido;
        }
         
        public static bool CumpleSpec(Microsoft.Office.Interop.Excel.Workbook ObjWorkBook, DXL_SpreadSheet hc, bool permiteCeldasVacias, bool buscarEquivalenciasTipos, out string msg)
        {
            bool cumple;
            int numHojasXL;
            int numHojasRequeridas;

            Microsoft.Office.Interop.Excel.Worksheet objWorkSheet;
            Microsoft.Office.Interop.Excel.Range rango;
            int contHoja;
            int contGrilla;
            string txt;

            cumple = true;
            msg = string.Empty;

            numHojasXL = 0;

            if ((ObjWorkBook != null) && (ObjWorkBook.Sheets != null))
            {
                numHojasXL = ObjWorkBook.Sheets.Count;
            }

            numHojasRequeridas = 0;

            if (hc != null)
            {
                numHojasRequeridas = hc.NumHojasRequeridas();
            }

            if (numHojasRequeridas > numHojasXL)
            {
                msg = string.Format("El número de hojas requeridas es {0} y el archivo contiene {1}", numHojasRequeridas, numHojasXL);
                cumple = false;
            }
            else if (numHojasRequeridas > 0)
            {

                contHoja = 0;

                objWorkSheet = null;

                try
                {
                    //buscar los titulos de hojas, no tienen que ir en el mismo orden
                    foreach (Hoja h in hc.Hojas)
                    {
                        txt = hc.Hojas[contHoja].Nombre;
                        objWorkSheet = PestañaDadoNombre(ObjWorkBook, txt, true);

                        if ((objWorkSheet == null) && (hc.Hojas[contHoja].EsRequerida))
                        {
                            msg = string.Format("La hoja/pestaña {0} no existe en el archivo de XL y es requerida", txt);
                            cumple = false;
                        }
                        else if (objWorkSheet != null)
                        {
                            if (h.Grillas != null)
                            {
                                contGrilla = 0;
                                foreach (Grilla g in h.Grillas)
                                {
                                    //verificar los TIPOS de las grillas
                                    string orig;
                                    string dest;

                                    //selecciona la hoja actual
                                    objWorkSheet.Select(Type.Missing);

                                    //string aux = "B9:K15";
                                    //ExcelLib.PartirRango(aux, out orig, out dest);

                                    g.PartirRango(out orig, out dest);

                                    rango = objWorkSheet.get_Range(orig, dest);

                                    if (rango != null)
                                    {
                                        object aux;

                                        int numCol = rango.Cells.Columns.Count;
                                        int numFil = rango.Cells.Rows.Count;

                                        int i;
                                        int j;
                                        string tipoEsperado;

                                        for (j = 1; (cumple && (j <= numFil)); j++)
                                        {
                                            for (i = 1; (cumple && (i <= numCol)); i++)
                                            {
                                                aux = rango.Cells[j, i].value;
                                                tipoEsperado = g.TiposColumnas[i - 1];
                                                cumple = CoincidenTipos(aux, tipoEsperado, permiteCeldasVacias, buscarEquivalenciasTipos);

                                                if (!cumple)
                                                {
                                                    msg = string.Format("La celda con valor \'{0}\' es de tipo {1} pero debe ser {2} -- Hoja #{3}, Grilla #{4}, Rango {5}, Fila={6}, Columna={7}",
                                                        aux,
                                                        g.TipoNombreAmigable(aux.GetType().ToString()),
                                                        g.TipoNombreAmigable(tipoEsperado),
                                                        contHoja + 1,
                                                        contGrilla + 1,
                                                        g.Rango, j, i);
                                                }
                                            }
                                            if (!cumple)
                                            {
                                                break;
                                            }
                                        }

                                    }
                                    else
                                    {
                                        msg = string.Format("No se pudo validar/no existe el rango {0}:{1} -- Hoja #{2}, Grilla #{3}",
                                            orig,
                                            dest,
                                            contHoja + 1,
                                            contGrilla + 1);

                                        cumple = false;
                                        break;
                                    }
                                    if (!cumple)
                                    {
                                        break;
                                    }
                                    contGrilla++;
                                }

                            }
                        }
                        else
                        {
                            //Es una hoja opcional que no existe en el archivo...todo bien !!
                        }
                        if (!cumple)
                        {
                            break;
                        }
                        contHoja++;
                    }

                }
                catch (Exception ex)
                {
                    if (msg == string.Empty)
                    {
                        msg = string.Format("Error tecnico {0}\n{1}", ex.Message, ex.StackTrace);
                    }
                    cumple = false;
                }

            }
            return cumple;
        }
        /* DEPENDENCIA DE EXCEL INSTALADO
        public static Microsoft.Office.Interop.Excel.Worksheet PestañaDadoNombre(Microsoft.Office.Interop.Excel.Workbook ObjWorkBook, string texto, bool identico)
        {
            int i;
            bool encontro;
            int numHojasXL;
            Microsoft.Office.Interop.Excel.Worksheet objWorkSheet;

            numHojasXL = ObjWorkBook.Sheets.Count;
            i = 0;
            objWorkSheet = null;

            do
            {
                objWorkSheet = (Microsoft.Office.Interop.Excel.Worksheet)ObjWorkBook.Sheets[i + 1];

                if (identico)
                {
                    encontro = (objWorkSheet.Name == texto);
                }
                else
                {
                    encontro = (objWorkSheet.Name.Trim().ToUpper() == texto.Trim().ToUpper());
                }
                if (!encontro)
                {
                    i++;
                }
            } while ((!encontro) && (i < numHojasXL));

            if (!encontro)
            {
                objWorkSheet = null;
            }
            return objWorkSheet;
        }
         *  FIN DE DEPENDENCIA DE EXCEL
        */
        private static bool CoincidenTipos(object aux, string tipoEsperado, bool permiteVacios, bool buscarEquivalenciasTipos)
        {
            bool tipoCorrecto;

            if ((aux == null) || (aux == DBNull.Value))
            {
                if (permiteVacios)
                {
                    tipoCorrecto = true;
                }
                else
                {
                    tipoCorrecto = false;
                }
            }
            else
            {
                Type tipo;

                tipo = aux.GetType();
                //verificar si Tiene el tipo exacto de datos
                tipoCorrecto = (tipo == Type.GetType(tipoEsperado));

                if ((!tipoCorrecto) && (buscarEquivalenciasTipos))
                {
                    //Buscar equivalencias permitidas
                    if (tipoEsperado == "System.Decimal")
                    {
                        tipoCorrecto = (tipo == Type.GetType("System.Double")) || (tipo == Type.GetType("System.DateTime"));
                    }
                    else if (tipoEsperado == "System.DateTime")
                    {
                        //Cuando la columna es de TIPO "DateTime" pero sus datos son Double y Decimal estaria bien 
                        //TODO este es un ARRREGLO RAPIDO mientras que se permite que los titulos tengan un tipo y sus datos tengan otro tipo
                        tipoCorrecto = (tipo == Type.GetType("System.Double")) || (tipo == Type.GetType("System.Decimal"));
                    }
                    else if (tipoEsperado == "System.String")
                    {
                        //si se esperaba TEXTO permitir cualquier tipo de dato en esa celda
                        tipoCorrecto = true;
                    }
                    /*
                else
                {
                    //para depurar otros casos
                    int X = 8;
                }
                     * */
                }
            }
            return tipoCorrecto;
        }

    }
}
